package com.dudy.authdemo.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Configuration
@Component
public class JwtTokenUtil {

    @Value("${jwt.secret:helowin}")
    private String secret = "helowin";
    // 24*60*60
    @Value("${jwt.expiration:86400}")
    private long expiration = 86400;

    /**
     * 生成令牌Token
     * 1. 建议使用唯一、可读性高的字段作为生成令牌的参数
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<String, Object>();
        return generateJWT(claims, username);
    }

    /**
     * 校验token
     * 1. 判断用户名和token包含的属性一致
     * 2. 判断token是否失效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return StringUtils.equalsAnyIgnoreCase(username, userDetails.getUsername()) && isInvalid(token);
    }

    /**
     * token 失效判断，依据如下：
     * 1. 关键字段被修改后token失效，包括密码修改、用户退出登录等
     * 2. token 过期失效
     */
    private Boolean isInvalid(String token) {
        Claims claims = parseJWT(token);
        Date issuedAt = claims.getIssuedAt();
        return issuedAt.before(new Date()) && isExpired(token);
    }

    /**
     * token 过期判断，常见逻辑有几种：
     * 1. 基于本地内存，问题是重启服务失效
     * 2. 基于数据库，常用的有Redis数据库，但是频繁请求也是不小的开支
     * 3. 用jwt的过期时间和当前时间做比较（推荐）
     */
    public Boolean isExpired(String token) {
        Claims claims = parseJWT(token);
        Date expiration = claims.getExpiration();
        return expiration.before(new Date());
    }


    /**
     * 从token 中获取用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = parseJWT(token);
        return claims.getSubject();
    }

    /**
     * 生成jwt方法
     */
    public String generateJWT(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)      // 定义属性
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) // 设置令牌有效期
                .signWith(SignatureAlgorithm.HS512, secret) // 使用指定的算法和密钥对jwt进行签名
                .compact();
    }

    /**
     * 解析jwt方法
     */
    private Claims parseJWT(String token) {
        return Jwts.parser()
                .setSigningKey(secret)  // 设置密钥
                .parseClaimsJws(token)  // 解析token
                .getBody();
    }

}